#include <neko.h>
#include <stdlib.h>
#include <stdio.h>
#include <iup.h>
#include <iupim.h>
#include <iupcbs.h>
#include <iupole.h>
#include <iuptabs.h>
#include <iupcontrols.h>

#import "progid:ShockwaveFlash.ShockwaveFlash" no_namespace named_guids

DEFINE_KIND( k_ihandle );
DEFINE_KIND( k_icallback );
DEFINE_KIND( k_iswf );

#define		IHANDLE( o )	(Ihandle*)val_data( o );
#define		ICALLBACK( o )	(Icallback*)val_data( o );

/*
 * callbacks
 */

int ngui_cb_action( Ihandle* self )
{
	val_call0( (value)IupGetAttribute( self, "ACTION_CBFUNC" ) );
	return IUP_DEFAULT;
}

int ngui_cb_close( Ihandle* self )
{
	val_call0( (value)IupGetAttribute( self, "CLOSE_CBFUNC" ) );
	return IUP_DEFAULT;
}

int ngui_cb_button( Ihandle* self, int but, int pressed, int x, int y, char* status )
{
	val_call1( (value)IupGetAttribute( self, "BUTTON_CBFUNC" ), alloc_int( pressed ) );
	if ( IupGetAttribute( self, "CBRETURN" ) != NULL && pressed == 0 )
		return val_int( (value)IupGetAttribute( self, "CBRETURN" ) );
	return IUP_DEFAULT;
}

int ngui_cb_tabs( Ihandle* self, Ihandle* newTab, Ihandle* oldTab )
{
	return val_int( val_call2( (value)IupGetAttribute( self, "TABS_CBFUNC" ), alloc_string( IupGetAttribute( newTab, "TABTITLE" ) ), alloc_string( IupGetAttribute( oldTab, "TABTITLE" ) ) ) );
	//return IUP_DEFAULT;
}

int ngui_cb_text(Ihandle *self, int c, char *after)
{
	return val_int( val_call2( (value)IupGetAttribute( self, "TEXT_CBFUNC" ), alloc_int( c ), alloc_string( after ) ) );
	//return IUP_DEFAULT;
}

/*
 * core functions
 */

value ngui_init()
{
	if (IupOpen() == IUP_ERROR || IupControlsOpen() == IUP_ERROR)
	{
		return val_false;
	}
	IupSetLanguage("ENGLISH");
	return val_true;
}

value ngui_main_loop()
{
	if (IupMainLoop() == IUP_ERROR)
	{
		return val_false;
	}
	return val_true;
}

value ngui_loop_step()
{
	if (IupLoopStep() == IUP_ERROR)
	{
		return val_false;
	}
	return val_true;
}
  
void ngui_close()
{
	IupControlsClose();
	IupClose();
}
  
void ngui_flush()
{
	IupFlush();
}

value ngui_get_url( value url )
{
	val_check( url, string );
	IupHelp( val_string( url ) );
	return val_null;
}

value ngui_map_font( value font )
{
	val_check( font, string );
	return alloc_string( IupMapFont( val_string ( font ) ) );
}

value ngui_map_font_reverse( value font )
{
	val_check( font, string );
	return alloc_string( IupUnMapFont( val_string ( font ) ) );
}

value ngui_release_element( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	IupDestroy( item );
	return val_null;
}

value ngui_get_focus()
{
	return alloc_abstract( k_ihandle, IupGetFocus() );
}

value ngui_next_focus( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupNextField( item ) );
}

value ngui_prev_focus( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupPreviousField( item ) );
}

value ngui_set_focus( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupSetFocus( item ) );
}

value ngui_get_fill()
{
	return alloc_abstract( k_ihandle, IupFill() );
}

value ngui_hide_element( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	if (IupHide( item ) == IUP_ERROR) return val_false;
	return val_true;
}

value ngui_show_element( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	if (IupShow( item ) == IUP_ERROR) return val_false;
	return val_true;
}

value ngui_show_element_xy( value element, value x, value y )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	val_check( x, int );
	val_check( y, int );
	item = IHANDLE( element );
	if (IupShowXY( item, val_int( x ), val_int( y ) ) == IUP_ERROR) return val_false;
	return val_true;
}

value ngui_map_element( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_int( IupMap( item ) );
}

value ngui_popup_element( value element, value x, value y )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_int( IupPopup( item, val_int( x ), val_int( y ) ) );
}

value ngui_detach_element( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	IupDetach( item );
	return val_null;
}

value ngui_append_element( value container, value element )
{
	Ihandle *item;
	Ihandle *box;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	val_check_kind( container, k_ihandle );
	box = IHANDLE( container );
	return alloc_abstract( k_ihandle, IupAppend( box, item ) );
}

value ngui_get_next_child( value parent, value lastchild )
{
	Ihandle *item;
	Ihandle *box;
	val_check_kind( lastchild, k_ihandle );
	item = IHANDLE( lastchild );
	val_check_kind( parent, k_ihandle );
	box = IHANDLE( parent );
	return alloc_abstract( k_ihandle, IupGetNextChild( box, item ) );
}

value ngui_get_type( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_string( IupGetType( item ) );
}

value ngui_set_handle( value element, value name )
{
	Ihandle *item;
	val_check( name, string );
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupSetHandle( val_string( name ), item ) );
}

value ngui_get_handle( value name )
{
	val_check( name, string );
	return alloc_abstract( k_ihandle, IupGetHandle( val_string( name ) ) );
}

value ngui_get_name( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_string( IupGetName( item ) );
}

value ngui_get_dialog( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupGetDialog( item ) );
}

value ngui_store_attribute( value element, value attrib, value val )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	val_check( attrib, string );
	val_check( val, string );
	IupStoreAttribute( item, val_string( attrib ), val_string( val ) );
	return val_null;
}

value ngui_set_attribute( value element, value attrib, value val )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	val_check( attrib, string );
	val_check( val, string );
	if ( val_is_string( val ) )
		IupSetAttribute( item, val_string( attrib ), val_string( val ) );
	else
		IupSetAttribute( item, val_string( attrib ), (char*)val );
	return val_null;
}

value ngui_set_attributes( value element, value attrib )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	val_check( attrib, string );
	IupSetAttributes( item, val_string( attrib ) );
	return val_null;
}

value ngui_get_attribute( value element, value attrib )
{
	Ihandle *item;
	char* ret;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	val_check( attrib, string );
	ret = IupGetAttribute( item, val_string( attrib ) );
	return alloc_string( ret );
}

value ngui_store_global( value attrib, value val )
{
	val_check( attrib, string );
	val_check( val, string );
	IupStoreGlobal( val_string( attrib ), val_string( val ) );
	return val_null;
}

value ngui_set_global( value attrib, value val )
{
	val_check( attrib, string );
	val_check( val, string );
	IupSetGlobal( val_string( attrib ), val_string( val ) );
	return val_null;
}

value ngui_get_global( value attrib )
{
	val_check( attrib, string );
	IupGetGlobal( val_string( attrib ) );
	return val_null;
}

value ngui_get_action_name()
{
	return alloc_string( IupGetActionName() );
}

value ngui_get_function( value action )
{
	val_check( action, string );
	//return alloc_function( IupGetFunction( val_string( action ) ) );
	return val_null;
}

value ngui_set_function( value action, value callback )
{
	val_check( action, string );
	val_check( callback, function );
	//return alloc_function( IupSetFunction( val_string( action ), val_function( callback ) ) );
	return val_null;
}

value ngui_set_callback( value element, value type, value func, value ret )
{
	Ihandle *item;
	Icallback cb;
	char* t;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	val_check( type, string );
	val_check( func, function );
	t = val_string( type );
	
	if ( strcmp( t, "BUTTON_CB" ) == 0 )
	{
		cb = (Icallback)ngui_cb_button;
		IupSetAttribute( item, "BUTTON_CBFUNC", (char*)func );
		if ( ! val_is_null( ret ) ) 
			IupSetAttribute( item, "CBRETURN", (char*)ret );
		
		IupSetCallback( item, "BUTTON_CB", (Icallback)cb );
	}
	else if ( strcmp( t, "TABS_CB" ) == 0 )
	{
		cb = (Icallback)ngui_cb_tabs;
		IupSetAttribute( item, "TABS_CBFUNC", (char*)func );
		
		IupSetCallback( item, "TABCHANGE_CB", (Icallback)cb );
	}
	else if ( strcmp( t, "CLOSE_CB" ) == 0 )
	{
		cb = (Icallback)ngui_cb_close;
		IupSetAttribute( item, "CLOSE_CBFUNC", (char*)func );
		
		IupSetCallback( item, "CLOSE_CB", (Icallback)cb );
	}
	else if ( strcmp( t, "TEXT_CB" ) == 0 )
	{
		cb = (Icallback)ngui_cb_text;
		IupSetAttribute( item, "TEXT_CBFUNC", (char*)func );

		IupSetCallback( item, "ACTION", (Icallback)cb );
	}
	else
	{
		cb = (Icallback)ngui_cb_action;
		IupSetAttribute( item, "ACTION_CBFUNC", (char*)func );

		IupSetCallback( item, "ACTION", (Icallback)cb );
	}
	return val_null;
}

value ngui_ole_container( value ax )
{
	Ihandle* control;
	IupOleControlOpen();
	val_check( ax, string );
	control = IupOleControl( val_string( ax ) );
	// Sets production mode
	IupSetAttribute(control, "DESIGNMODE", IUP_NO);
	return alloc_abstract( k_ihandle, control );
}

value ngui_get_flash_hnd( value iupswf )
{
	Ihandle *item;
	IUnknown *punk;
	IShockwaveFlash *pswf;
	val_check_kind( iupswf, k_ihandle );
	item = IHANDLE( iupswf );

	punk = (IUnknown*) IupGetAttribute(item, "IUNKNOWN");
	pswf = NULL;
	punk->QueryInterface(IID_IShockwaveFlash, (void **)&pswf);
	punk->Release();

	IupSetAttribute(item, "MY_SWF", (char*)pswf);

	return alloc_abstract( k_iswf, pswf );
}

value ngui_flash_load_url( value swf, value url )
{
	IShockwaveFlash *pswf;
	val_check( url, string );
	val_check_kind( swf, k_iswf );
	
	pswf = (IShockwaveFlash*)val_data( swf );

	pswf->WMode = L"Transparent";
	pswf->Movie = val_string( url );
	pswf->Play();

	return alloc_int( 0 );
}

value ngui_get_screensize()
{
	return alloc_string( IupGetGlobal( "SCREENSIZE" ) );
}

value ngui_load_image( value f )
{
	val_check( f, string );
	Ihandle* im = IupLoadImage( val_string( f ) );
	if ( im == NULL )
		return val_null;
	else
		return alloc_abstract( k_ihandle, im );
}

DEFINE_PRIM(ngui_load_image,1);
DEFINE_PRIM(ngui_get_screensize,0);
DEFINE_PRIM(ngui_init,0);
DEFINE_PRIM(ngui_main_loop,0);
DEFINE_PRIM(ngui_loop_step,0);
DEFINE_PRIM(ngui_close,0);
DEFINE_PRIM(ngui_flush,0);
DEFINE_PRIM(ngui_get_url,1);
DEFINE_PRIM(ngui_map_font,1);
DEFINE_PRIM(ngui_map_font_reverse,1);
DEFINE_PRIM(ngui_release_element,1);
DEFINE_PRIM(ngui_get_focus,0);
DEFINE_PRIM(ngui_set_focus,1);
DEFINE_PRIM(ngui_get_fill,0);
DEFINE_PRIM(ngui_next_focus,1);
DEFINE_PRIM(ngui_prev_focus,1);
DEFINE_PRIM(ngui_hide_element,1);
DEFINE_PRIM(ngui_show_element,1);
DEFINE_PRIM(ngui_show_element_xy,3);
DEFINE_PRIM(ngui_map_element,1);
DEFINE_PRIM(ngui_popup_element,3);
DEFINE_PRIM(ngui_detach_element,1);
DEFINE_PRIM(ngui_append_element,2);
DEFINE_PRIM(ngui_get_next_child,2);
DEFINE_PRIM(ngui_get_type,1);
DEFINE_PRIM(ngui_set_handle,2);
DEFINE_PRIM(ngui_get_handle,1);
DEFINE_PRIM(ngui_get_name,1);
DEFINE_PRIM(ngui_get_dialog,1);
DEFINE_PRIM(ngui_store_attribute,3);
DEFINE_PRIM(ngui_set_attribute,3);
DEFINE_PRIM(ngui_set_attributes,2);
DEFINE_PRIM(ngui_get_attribute,2);
DEFINE_PRIM(ngui_store_global,2);
DEFINE_PRIM(ngui_set_global,2);
DEFINE_PRIM(ngui_get_global,1);
DEFINE_PRIM(ngui_get_action_name,0);
DEFINE_PRIM(ngui_get_function,1);
DEFINE_PRIM(ngui_set_function,2);
DEFINE_PRIM(ngui_set_callback,4);
DEFINE_PRIM(ngui_ole_container,1);
DEFINE_PRIM(ngui_get_flash_hnd,1);
DEFINE_PRIM(ngui_flash_load_url,2);


/*
 * Elements
 */

value ngui_element_button( value title, value action )
{
	val_check( title, string );
	if ( val_is_string( action ) )
		return alloc_abstract( k_ihandle, IupButton( val_string( title ), val_string( action ) ) );
	else
		return alloc_abstract( k_ihandle, IupButton( val_string( title ), NULL ) );
}

value ngui_element_canvas( value action )
{
	val_check( action, string );
	return alloc_abstract( k_ihandle, IupCanvas( val_string( action ) ) );
}

value ngui_element_color( value r, value g, value b )
{
	val_check( r, int );
	val_check( g, int );
	val_check( b, int );
	return alloc_abstract( k_ihandle, IupColor( val_int( r ), val_int( g ), val_int( b ) ) );
}

value ngui_element_frame( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupFrame( item ) );
}

value ngui_element_image( value width, value height, value pixels )
{
	val_check( width, int );
	val_check( height, int );
	val_check( pixels, string );
	return alloc_abstract( k_ihandle, IupImage( val_int( width ), val_int( height ), val_string( pixels ) ) );
}

value ngui_element_image_load( value filename )
{
	Ihandle *file;
	val_check( filename, string );
	file = IupLoadImage( val_string( filename ) );
	if ( file == NULL )
		printf( "unable to load file: %s", val_string( filename ) );
	return alloc_abstract( k_ihandle, file );
}

value ngui_element_label( value title )
{
	val_check( title, string );
	return alloc_abstract( k_ihandle, IupLabel( val_string( title ) ) );
}

value ngui_element_list( value action )
{
	val_check( action, string );
	return alloc_abstract( k_ihandle, IupList( val_string( action ) ) );
}

value ngui_element_multiline( value action )
{
	val_check( action, string );
	return alloc_abstract( k_ihandle, IupMultiLine( val_string( action ) ) );
}

value ngui_element_text( value action )
{
	val_check( action, string );
	return alloc_abstract( k_ihandle, IupText( val_string( action ) ) );
}

value ngui_element_timer()
{
	return alloc_abstract( k_ihandle, IupTimer() );
}

value ngui_element_toggle( value title, value action )
{
	val_check( title, string );
	val_check( action, string );
	return alloc_abstract( k_ihandle, IupToggle( val_string( title ), val_string( action ) ) );
}

value ngui_element_dialog( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupDialog( item ) );
}

value ngui_element_file_dialog()
{
	return alloc_abstract( k_ihandle, IupFileDlg() );
}

value ngui_element_alarm( value title, value message, value b1, value b2, value b3 )
{
	val_check( title, string );
	val_check( message, string );
	val_check( b1, string );
	return alloc_int( IupAlarm( val_string( title ), val_string( message ), val_string( b1 ), val_is_null( b2 ) ? NULL : val_string( b2 ), val_is_null( b3 ) ? NULL : val_string( b3 ) ) );
}

value ngui_element_get_file( value filename )
{
	char *t;
	val_check( filename, string );
	t = val_string( filename );
	IupGetFile( t );
	return alloc_string( t );
}

value ngui_element_get_text( value title, value text )
{
	char *t;
	val_check( title, string );
	val_check( text, string );
	t = val_string( text );
	IupGetText( val_string( title ), t );
	return alloc_string( t );
}

value ngui_element_message( value title, value message )
{
	val_check( title, string );
	val_check( message, string );
	IupMessage( val_string( title ), val_string( message ) );
	return val_null;
}

value ngui_element_fill()
{
	return alloc_abstract( k_ihandle, IupFill() );
}

value ngui_element_hbox()
{
	return alloc_abstract( k_ihandle, IupHbox( NULL ) );
}

value ngui_element_vbox()
{
	return alloc_abstract( k_ihandle, IupVbox( NULL ) );
}

value ngui_element_zbox()
{
	return alloc_abstract( k_ihandle, IupZbox( NULL ) );
}

value ngui_element_tabs( value elements )
{
	val_check( elements, array );
	value* varr = val_array_ptr( elements );
	int size = val_array_size( elements );
	Ihandle **arr = (Ihandle**)malloc( ( size + 1 ) * sizeof( Ihandle* ) );
	for ( int i=0; i<size; i++ )
		arr[ i ] = (Ihandle*)val_data( varr[ i ] );
	arr[ size ] = NULL;
	return alloc_abstract( k_ihandle, IupTabsv( (Ihandle**)arr ) );
}

value ngui_element_radio( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupRadio( item ) );
}

value ngui_element_cbox( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_abstract( k_ihandle, IupCbox( item ) );
}

value ngui_element_item( value title, value action )
{
	val_check( title, string );
	if ( val_is_string( action ) )
		return alloc_abstract( k_ihandle, IupItem( val_string( title ), val_string( action ) ) );
	else
		return alloc_abstract( k_ihandle, IupItem( val_string( title ), NULL ) );
}

value ngui_element_map( value element )
{
	Ihandle *item;
	val_check_kind( element, k_ihandle );
	item = IHANDLE( element );
	return alloc_int( IupMap( item ) );
}

value ngui_element_menu()
{
	return alloc_abstract( k_ihandle, IupMenu( NULL ) );
}

value ngui_element_separator()
{
	return alloc_abstract( k_ihandle, IupSeparator() );
}

value ngui_element_submenu( value title, value menu )
{
	Ihandle *item;
	val_check_kind( menu, k_ihandle );
	val_check( title, string );
	item = IHANDLE( menu );
	return alloc_abstract( k_ihandle, IupSubmenu( val_string( title ), item ) );
}

value ngui_element_color_browser()
{
	return alloc_abstract( k_ihandle, IupColorBrowser() );
}

value ngui_element_dial( value type )
{
	char *stype;
	val_check( type, int );
	switch ( val_int( type ) )
	{
		case 1:
			stype = "HORIZONTAL";
			break;
		case 2:
			stype = "VERTICAL";
			break;
		default:
			stype = "CIRCULAR";
			break;
	}
	return alloc_abstract( k_ihandle, IupDial( stype ) );
}

value ngui_element_gauge()
{
	return alloc_abstract( k_ihandle, IupGauge() );
}

value ngui_element_get_color( value x, value y )
{
	unsigned char *r = 0;
	unsigned char *g = 0;
	unsigned char *b = 0;
	val_check( x, int );
	val_check( y, int );
	if (IupGetColor( val_int( x ), val_int( y ), r, g, b ) == 1)
		return alloc_int( ((int)*r)<<16 | ((int)*g)<<8 | ((int)*b) );
	else
		return alloc_int( -1 );
}

DEFINE_PRIM(ngui_element_button,2);
DEFINE_PRIM(ngui_element_canvas,1);
DEFINE_PRIM(ngui_element_color,3);
DEFINE_PRIM(ngui_element_frame,1);
DEFINE_PRIM(ngui_element_image,3);
DEFINE_PRIM(ngui_element_label,1);
DEFINE_PRIM(ngui_element_list,1);
DEFINE_PRIM(ngui_element_multiline,1);
DEFINE_PRIM(ngui_element_text,1);
DEFINE_PRIM(ngui_element_timer,0);
DEFINE_PRIM(ngui_element_toggle,2);
DEFINE_PRIM(ngui_element_dialog,1);
DEFINE_PRIM(ngui_element_file_dialog,0);
DEFINE_PRIM(ngui_element_alarm,5);
DEFINE_PRIM(ngui_element_get_file,1);
DEFINE_PRIM(ngui_element_get_text,2);
DEFINE_PRIM(ngui_element_message,2);
DEFINE_PRIM(ngui_element_fill,0);
DEFINE_PRIM(ngui_element_hbox,0);
DEFINE_PRIM(ngui_element_vbox,0);
DEFINE_PRIM(ngui_element_zbox,0);
DEFINE_PRIM(ngui_element_tabs,1);
DEFINE_PRIM(ngui_element_radio,1);
DEFINE_PRIM(ngui_element_item,2);
DEFINE_PRIM(ngui_element_menu,0);
DEFINE_PRIM(ngui_element_separator,0);
DEFINE_PRIM(ngui_element_submenu,2);
DEFINE_PRIM(ngui_element_color_browser,0);
DEFINE_PRIM(ngui_element_dial,1);
DEFINE_PRIM(ngui_element_gauge,0);
DEFINE_PRIM(ngui_element_get_color,2);
DEFINE_PRIM(ngui_element_image_load,1);
DEFINE_PRIM(ngui_element_map,1);